﻿using System;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using AutoMock.Scenarios.Customer;
using Rhino.Mocks;
using Rhino.Mocks.Exceptions;

namespace AutoMock
{
	[TestClass]
	public class AutoMockerTests
	{
		[TestMethod]
		public void UpdateCustomerName_should_write_to_log()
		{
			var auto = new RhinoAutoMocker<CustomerUpdater>();
			auto.Get<ICustomerDataProvider>().Expect(x => x.GetCustomer(0)).IgnoreArguments().Return(new CustomerItem());

			auto.ClassUnderTest.UpdateCustomerName(1, "Frank", "frank@somecompany.com");

			auto.Get<ILogWriter>().AssertWasCalled(x => x.Write(null), x => x.IgnoreArguments());
		}
		[TestMethod]
		public void UpdateCustomerName_email_should_be_sent()
		{
			var autoMocker = new RhinoAutoMocker<CustomerUpdater>();
			autoMocker.Get<ICustomerDataProvider>().Expect(x => x.GetCustomer(0)).IgnoreArguments().Return(new CustomerItem());

			autoMocker.ClassUnderTest.UpdateCustomerName(1, "Frank", "frank@somecompany.com");

			autoMocker.Get<IMailSender>().AssertWasCalled(x => x.SendMail(null, null), x => x.IgnoreArguments());
		}
		[TestMethod]
		public void UpdateCustomerName_without_AutoMock_email_should_be_sent()
		{
			var customerData = MockRepository.GenerateStub<ICustomerDataProvider>();
			customerData.Expect(x => x.GetCustomer(0)).IgnoreArguments().Return(new CustomerItem());
			var mailSender = MockRepository.GenerateStub<IMailSender>();
			var logWriter = MockRepository.GenerateStub<ILogWriter>();

			var customerUpdater = new CustomerUpdater(customerData, mailSender, logWriter,null);

			customerUpdater.UpdateCustomerName(1, "Frank", "frank@somecompany.com");

			mailSender.AssertWasCalled(x => x.SendMail(null, null), x => x.IgnoreArguments());
		}

        [TestMethod]
        public void WhenInjectingDependencyManuallyNoMockShouldBeCreated()
        {
            var autoMocker = new RhinoAutoMocker<CustomerUpdater>(new DummyLogWriter());
            autoMocker.Get<ICustomerDataProvider>().Expect(x => x.GetCustomer(0)).IgnoreArguments().Return(new CustomerItem());

            autoMocker.ClassUnderTest.UpdateCustomerName(1, "Frank", "frank@somecompany.com");

            var dummyLogWriter = (DummyLogWriter)autoMocker.Get<ILogWriter>();

            Assert.AreEqual("Customer Frank has been updated", dummyLogWriter.LogEntrys.First());

        }

        [TestMethod]
        public void WhenInjectingDependencyArrayManuallyNoMockShouldBeCreated()
        {
            var instance = new IWriter[1];
            instance.SetValue(new NullWriter(),0);

            var autoMocker = new RhinoAutoMocker<CustomerUpdater>((object)instance);
            autoMocker.Get<ICustomerDataProvider>().Expect(x => x.GetCustomer(0)).IgnoreArguments().Return(new CustomerItem());

            autoMocker.ClassUnderTest.UpdateCustomerName(1, "Frank", "frank@somecompany.com");

            var dummyLogWriter = (IWriter[])autoMocker.Get<IWriter[]>();

            Assert.AreEqual(dummyLogWriter.First().GetHashCode() , instance[0].GetHashCode());

        }


        [TestMethod]
        public void ShouldThrowExceptionWhenTryingToUseInterfaceAsClassUnderTest()
        {
            ExceptionAssert.Throws<Exception>(() => { new RhinoAutoMocker<ILogWriter>(); }, "Interface cannot be used as ClassUnderTest. Only concrete class implementations are supported.");
        }

        [TestMethod]
        public void ShouldSupportOrderedTest()
        {
            //Arrange
            var autoMocker = new RhinoAutoMocker<CustomerUpdater>();

            var mockRepository = autoMocker.Repository;

            using (mockRepository.Ordered())
            {
                autoMocker.Get<ICustomerDataProvider>().Expect(x => x.GetCustomer(Arg<int>.Is.Anything)).Return(new CustomerItem());
                autoMocker.Get<ICustomerDataProvider>().Expect(x => x.UpdateCustomer(Arg<CustomerItem>.Is.Anything));
                autoMocker.Get<ILogWriter>().Expect(x => x.Write(Arg<string>.Is.Anything));
                autoMocker.Get<ILogWriter>().Expect(x => x.Write(Arg<string>.Is.Anything));
                autoMocker.Get<IMailSender>().Expect(x => x.SendMail(Arg<string>.Is.Anything, Arg<string>.Is.Anything));
            }

            //Act
            autoMocker.ClassUnderTest.UpdateCustomerName(1, "Frank", "frank@somecompany.com");

            //Assert
            ExceptionAssert.Throws<ExpectationViolationException>(mockRepository.VerifyAll, "IMailSender.SendMail(anything, anything); Expected #1, Actual #0.\r\nILogWriter.Write(anything); Expected #1, Actual #0.\r\n");

        }


        [TestMethod]
        public void ShouldSetupMockBeforeCreatingClassUnderTest()
        {
            //Arrange
            var autoMocker = new RhinoAutoMocker<CustomerUpdaterWithConstructorDependendOnMock>();

            var mockRepository = autoMocker.Repository;

            using (mockRepository.Ordered())
            {
                autoMocker.Get<ICustomerDataProvider>().Expect(x => x.GetCustomer(Arg<int>.Is.Anything)).Return(new CustomerItem(){ Address = "My address"});
            }

            //Act
            var returnAddress = autoMocker.ClassUnderTest.ReturnAddress();

            //Assert
            Assert.AreEqual("My address",returnAddress);

        }
	}
}
